package org.bt.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import lombok.extern.slf4j.Slf4j;
import org.bt.gateway.constant.MessageConstant;
import org.bt.gateway.entity.Error;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;

import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.List;

/**
 * Sentinel网关限流配置
 *
 * @author zwj
 * @date 2021/8/25
 */
@Configuration
@Slf4j
public class SentinelGatewayConfig {

    @Autowired
    private MessageSource messageSource;

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public SentinelGatewayConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                 ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }


    /**
     * 配置初始化的限流参数
     * 用于指定资源的限流规则.
     * 1.资源名称 (路由id)
     * 2.配置统计时间
     * 3.配置限流阈值
     */
    @PostConstruct
    public void initGatewayRules() {
//        Set<GatewayFlowRule> rules = new HashSet<>();
//        rules.add(new GatewayFlowRule("ft-boot-demo-general-route")
//                .setCount(1)
//                .setIntervalSec(1)
//        );
//        GatewayRuleManager.loadRules(rules);
    }

    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockHandler = (serverWebExchange, e) -> {
            Error error = new Error();
            if (e instanceof FlowException) {
                error.setCode(MessageConstant.SENTINEL_ERR_L_GATEWAY_FLOW);
                error.setMessage(messageSource.getMessage(MessageConstant.SENTINEL_ERR_L_GATEWAY_FLOW, null, null, LocaleContextHolder.getLocale()));
                error.setDetails(((FlowException) e).getRule().toString());
            } else if (e instanceof DegradeException) {
                error.setCode(MessageConstant.SENTINEL_ERR_L_GATEWAY_DEGRADE);
                error.setMessage(messageSource.getMessage(MessageConstant.SENTINEL_ERR_L_GATEWAY_DEGRADE, null, null, LocaleContextHolder.getLocale()));
                error.setDetails(((DegradeException) e).getRule().toString());
            } else if (e instanceof ParamFlowException) {
                error.setCode(MessageConstant.SENTINEL_ERR_L_GATEWAY_PARAM_FLOW);
                error.setMessage(messageSource.getMessage(MessageConstant.SENTINEL_ERR_L_GATEWAY_PARAM_FLOW, null, null, LocaleContextHolder.getLocale()));
                error.setDetails(((ParamFlowException) e).getRule().toString());
            } else if (e instanceof SystemBlockException) {
                error.setCode(MessageConstant.SENTINEL_ERR_L_GATEWAY_SYSTEM_BLOCK);
                error.setMessage(messageSource.getMessage(MessageConstant.SENTINEL_ERR_L_GATEWAY_SYSTEM_BLOCK, null, null,
                        LocaleContextHolder.getLocale()));
                error.setDetails(((SystemBlockException) e).getRule().toString());
            } else if (e instanceof AuthorityException) {
                error.setCode(MessageConstant.SENTINEL_ERR_L_GATEWAY_AUTHORITY);
                error.setMessage(messageSource.getMessage(MessageConstant.SENTINEL_ERR_L_GATEWAY_AUTHORITY, null, null, LocaleContextHolder.getLocale()));
                error.setDetails(((AuthorityException) e).getRule().toString());
            } else {
                error.setCode("COMMON_ERR_L_UNKNOWN");
                error.setMessage(e.getMessage());
            }

            return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(error));
        };
        GatewayCallbackManager.setBlockHandler(blockHandler);
    }

}
